package com.xuecheng.content.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.TeachplanConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.ext.TeachplanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程计划 服务实现类
 * </p>
 *
 * @author 黄子弟
 */
@Slf4j
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {

    @Autowired
    private CourseBaseServiceImpl courseBaseService;

    @Autowired
    private TeachplanMediaService teachplanMediaService;

    /*
     * 业务分析：
     *  1.判断关键数据：操作时的必要数据（一般是由前端来传递）
     *       courseId  companyId
     *  2.判断业务数据：操作时，判断数据的状态（一般是判断数据值，数据值来源于后端的数据源）
     *     课程基本信息
     *           判断是否是同一家教学机构
     *           判断课程是否删除
     *  3.获得课程下的所有课程计划数据
     *  4.通过java的递归来生成课程计划树形结构
     *  5.将node数据转为dto并返回
     *
     * */
    @Override
    public TeachplanDTO getTeachplan(Long courseBaseId, Long companyId) {
        //判断关键数据
        if (ObjectUtils.isEmpty(courseBaseId)) {
            ExceptionCast.cast(ContentErrorCode.E_120009);
        }
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120021);
        }
        //判断业务数据
        courseBaseService.getCourseBase(courseBaseId, companyId);

        //获得课程下的所有课程计划数据
        List<TeachplanNode> teachplanNodes = baseMapper.selectByCourseBaseId(courseBaseId);

        //通过java的递归来生成课程计划树形结构
        TeachplanDTO dto =null;
        if (!CollectionUtils.isEmpty(teachplanNodes)) {
            //得到根节点
            TeachplanNode rootNode = teachplanNodes.remove(0);
            getTeachplanTree(rootNode, teachplanNodes);
            dto = TeachplanConvert.INSTANCE.node2dto(rootNode);
        }else {
            dto = new TeachplanDTO();
        }
        return dto;
    }

    @Override
    @Transactional
    public TeachplanDTO createOrModifyTeachplan(TeachplanDTO teachplanDTO, Long companyId) {
        //关键数据
        if (ObjectUtils.isEmpty(teachplanDTO.getPname())) {
            ExceptionCast.cast(ContentErrorCode.E_120413);
        }
        TeachplanDTO dto = null;
        if (ObjectUtils.isEmpty(teachplanDTO.getTeachPlanId())) {
             dto  =  createTeachplan(teachplanDTO,companyId);
        } else {
            dto = modifyTeachplan(teachplanDTO,companyId);
        } 
        return dto;
    }


    /*
    *1.判断关键数据
    *     课程id，机构id
    * 2.判断业务数据
    *   课程基础信息
    *   课程计划信息（是否存在）
    *       判断几级数据
    *              二级下不能有子级目录才能删除
    *               三级下不能有关联的媒介资源才能删除
     */
    @Override
    @Transactional
    public void removeTeachplan(Long teachPlanId, Long companyId) {
        if (ObjectUtils.isEmpty(teachPlanId)|| ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120023);
        }
        Teachplan teachplan = this.getById(teachPlanId);
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120408);
        }
        CourseBase courseBase = courseBaseService.getCourseBase(teachplan.getCourseId(), companyId);
        //审核状态
        String auditStatus = courseBase.getAuditStatus();
        if (auditStatus.equals(CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode()) ||
                auditStatus.equals(CourseAuditEnum.AUDIT_PASTED_STATUS.getCode()) ||
                auditStatus.equals(CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode()))
        {
            ExceptionCast.cast(ContentErrorCode.E_120014);
        }

        //判断是几级数据
        if (teachplan.getGrade().equals(TeachPlanEnum.SECEND_LEVEL)) {
            //二级
            LambdaUpdateWrapper<Teachplan> wrapper = Wrappers.lambdaUpdate(Teachplan.class);
            wrapper.eq(Teachplan::getParentid, teachplan.getId());
            int count = this.count(wrapper);
            if (count>0) {
                ExceptionCast.cast(ContentErrorCode.E_120409);
            }
            boolean remove = this.removeById(teachplan.getId());
            if (!remove) {
                ExceptionCast.cast(ContentErrorCode.E_120407);
            }

        }

        if (teachplan.getGrade().equals(TeachPlanEnum.THIRD_LEVEL)) {
            //三级
            if (courseBase.getTeachmode().equals(CourseModeEnum.COURSE_MODE_RECORD_STATUS.getCode())) {
                //录播
                LambdaQueryWrapper<TeachplanMedia> wrapper = Wrappers.lambdaQuery(TeachplanMedia.class);
                wrapper.eq(TeachplanMedia::getTeachplanId, teachplan.getId());
                int count = teachplanMediaService.count(wrapper);
                if (count>0) {
                    ExceptionCast.cast(ContentErrorCode.E_120414);
                }

            }

            boolean remove = this.removeById(teachplan.getId());
            if (!remove) {
                ExceptionCast.cast(ContentErrorCode.E_120407);
            }

        }


    }


    /*
     * 业务分析：
     *   1.判断关键数据
     *       pname
     *       courseid
     *       teachplanid（不需要）
     *
     *   2.判断业务数据
     *       课程基本信息
     *
     *       课程计划
     *
     *   3.修改课程计划数据
     *       pname
     *       mediatype
     *       startime
     *       endtime
     *       decription
     *       id_preview
     *       判断修改后的结果
     *
     *   4.将数据库最新数据转为dto并返回
     *
     * */
    private TeachplanDTO modifyTeachplan(TeachplanDTO teachplanDTO, Long companyId) {
        //关键数据
        if (ObjectUtils.isEmpty(teachplanDTO.getCourseId())) {
            ExceptionCast.cast(ContentErrorCode.E_120415);
        }
        //业务数据
        CourseBase courseBase = courseBaseService.getCourseBase(teachplanDTO.getCourseId(), companyId);
        //审核状态
        String auditStatus = courseBase.getAuditStatus();
        if (auditStatus.equals(CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode()) ||
                auditStatus.equals(CourseAuditEnum.AUDIT_PASTED_STATUS.getCode()) ||
                auditStatus.equals(CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode()))
        {
            ExceptionCast.cast(ContentErrorCode.E_120014);
        }

        //课程计划
        Teachplan teachplan = this.getById(teachplanDTO.getTeachPlanId());
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        if (teachplan.getStatus().equals(TeachPlanEnum.FIRST_PARENTID_FLAG)) {
            ExceptionCast.cast(ContentErrorCode.E_120412);
        }

        //修改课程计划数据
        LambdaUpdateWrapper<Teachplan> wrapper = Wrappers.lambdaUpdate(Teachplan.class);
        wrapper.set(Teachplan::getPname, teachplanDTO.getPname())
                .set(Teachplan::getMediaType, teachplanDTO.getMediaType())
                .set(Teachplan::getStartTime, teachplanDTO.getStartTime())
                .set(Teachplan::getEndTime, teachplanDTO.getEndTime())
                .set(Teachplan::getDescription, teachplanDTO.getDescription())
                .set(Teachplan::getIsPreview, teachplanDTO.getIsPreview())
                .eq(Teachplan::getId, teachplanDTO.getTeachPlanId());
        boolean update = this.update(wrapper);
        if (!update) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }

        Teachplan po = this.getById(teachplanDTO.getTeachPlanId());

        TeachplanDTO dto = TeachplanConvert.INSTANCE.entity2dto(po);

        return dto;

    }

    /*
     * 创建课程计划信息
     *   前端添加课程计划：2级和3级数据
     *
     *   1.判断业务数据
     *       课程基本型
     *
     *   2.给添加课程计划关联父级数据
     *       获得父级数据
     *
     *   3.添加teachplan其他的数据
     *       grade
     *       orderby
     *
     *   4.保存课程计划数据
     *       判断保存后的结果
     *
     *   5.转换数据为dto并返回
     *
     * */
    private TeachplanDTO createTeachplan(TeachplanDTO teachplanDTO, Long companyId) {
        //判断业务数据
        CourseBase courseBase = courseBaseService.getCourseBase(teachplanDTO.getCourseId(), companyId);

        //获得父级数据
       Teachplan parentNode =  getParentNode(teachplanDTO,courseBase);

        //保存新增的课程计划数据
        Teachplan po = TeachplanConvert.INSTANCE.dto2entity(teachplanDTO);
        po.setParentid(parentNode.getId());
        po.setGrade(parentNode.getGrade()+1);
        LambdaQueryWrapper<Teachplan> wrapper = Wrappers.lambdaQuery(Teachplan.class);
        wrapper.eq(Teachplan::getParentid, parentNode.getId());
        int count = this.count(wrapper);
        po.setOrderby((count+1));

        boolean result = this.save(po);
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }

        Teachplan teachplan = this.getById(po.getId());
        TeachplanDTO dto = TeachplanConvert.INSTANCE.entity2dto(teachplan);

        return dto;


    }

    /*
     *  获得父级数据内容
     *   1.判断获得的是二级或三级课程计划父级数据
     *       是否有parentid
     *           有：三级
     *           没有：二级
     *   2.获得二级课程计划的父级数据
     *       查询一级课程计划数据：parentid=0，courseid
     *           如果没有：
     *               添加一级课程计划
     *           如果有：
     *               直接返回
     *
     *   3.获得三级课程计划的父级数据
     *       根据parentid查询二级课程计划
     *           如果没有：
     *               抛出异常
     *           如果有：
     *               直接返回
     * */

    private Teachplan getParentNode(TeachplanDTO teachplanDTO, CourseBase courseBase) {
        //判断传入的是2级或3级数据
        if (ObjectUtils.isEmpty(teachplanDTO.getParentid())) {
            //2级
            LambdaQueryWrapper<Teachplan> wrapper = Wrappers.lambdaQuery(Teachplan.class);
            wrapper.eq(Teachplan::getCourseId,teachplanDTO.getCourseId())
                    .eq(Teachplan::getParentid,TeachPlanEnum.FIRST_PARENTID_FLAG);
            Teachplan teachplan = this.getOne(wrapper);
            if (ObjectUtils.isEmpty(teachplan)) {
                //添加一级课程计划
                Teachplan teachplanNode = new Teachplan();
                teachplanNode.setPname(teachplanDTO.getPname());
                teachplanNode.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                teachplanNode.setGrade(TeachPlanEnum.FIRST_LEVEL);
                teachplanNode.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                teachplanNode.setCourseId(courseBase.getId());
                boolean save = this.save(teachplanNode);
                if (!save) {
                    ExceptionCast.cast(ContentErrorCode.E_120407);
                }
                Teachplan parentNode = this.getById(teachplanNode.getId());
                return parentNode;
            } else {
                //直接返回
                return teachplan;
            }
        } else {
            //3级
            Long parentid = teachplanDTO.getParentid();
            LambdaQueryWrapper<Teachplan> wrapper = Wrappers.lambdaQuery(Teachplan.class);
            wrapper.eq(Teachplan::getId, parentid);
            Teachplan teachplan = this.getOne(wrapper);
            if (ObjectUtils.isEmpty(teachplan)) {
                ExceptionCast.cast(ContentErrorCode.E_120408);
            }

            return teachplan;

        }
    }

    private void getTeachplanTree(TeachplanNode parentNode,List<TeachplanNode> teachplanNodes) {
        //递归方式实现树形结构
        List<TeachplanNode> childrenNodes = parentNode.getChildrenNodes();
        if (ObjectUtils.isEmpty(childrenNodes)) {
            childrenNodes = new ArrayList<TeachplanNode>();
        }

        for (TeachplanNode node : teachplanNodes) {
            if (ObjectUtils.nullSafeEquals(parentNode.getId(),node.getParentid())) {
                childrenNodes.add(node);
                parentNode.setChildrenNodes(childrenNodes);
                if (!(TeachPlanEnum.THIRD_LEVEL.equals(node.getGrade()))) {
                    getTeachplanTree(node, teachplanNodes);
                } else {
                    //判断是否关联媒资信息
                    LambdaQueryWrapper<TeachplanMedia> wrapper = Wrappers.lambdaQuery(TeachplanMedia.class);
                    wrapper.eq(TeachplanMedia::getTeachplanId, node.getId());
                    wrapper.eq(TeachplanMedia::getCourseId, node.getCourseId());

                    TeachplanMedia teachplanMedia = teachplanMediaService.getOne(wrapper);
                    if (!ObjectUtils.isEmpty(teachplanMedia)) {
                        node.setTeachplanMedia(teachplanMedia);
                    }
                }
            }
        }

    }
}
